// DESCRIPTION: Verilator: Verilog example module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2003 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
// ======================================================================

// This is intended to be a complex example of several features, please also
// see the simpler examples/make_hello_c.

module top (
   // Declare some signals so we can see how I/O works
    input  wire         i_clk
,   input  wire         i_rst_n         
,   input  wire [31:0]  i_start_pc
,   input  wire [2 :0]  i_excp_mask_config
,   output reg  [1 :0]  o_core_status
,   output reg  [2 :0]  o_exceptions
,   output reg  [31:0]  o_exceptions_pc
,   output reg          o_sim_end
);
localparam   DAT_W  = 32            ;
localparam  ADDR_W  = 10            ;   // 1024 x 32-bit = 4KB  
localparam   COL_W  = 8             ;   // 1x Byte  
localparam   ENA_W  = DAT_W/COL_W   ;   // 1x Byte  
localparam  MAX_CYC = 50000         ;

reg             i_start_up      ;
wire [1 :0]     core_status     ;
wire [2 :0]     exceptions      ;
wire [31:0]     exceptions_pc   ;

wire                dmem_ce     ;   // active high 
wire    [ENA_W-1:0] dmem_we     ;   // 1: byte enable write, all 0: read 
wire   [ADDR_W-1:0] dmem_addr   ;       
wire    [DAT_W-1:0] dmem_din    ;

reg  [31:0]     wait_cnt        ;

always @(posedge i_clk or negedge i_rst_n) begin
    if (i_rst_n == 1'b0) begin
        wait_cnt        <= 32'd0    ;
        o_core_status   <= 2'd0     ;
        o_exceptions    <= 3'd0     ;
        o_exceptions_pc <= 32'd0    ;
        i_start_up      <= 1'b0     ;
    end
    else begin 
        if (wait_cnt < 32'd100) begin
            wait_cnt    <= wait_cnt + 32'd1 ;
        end

        if (wait_cnt > 32'd10) begin
            i_start_up  <= 1'b1     ; 
        end
        else if (wait_cnt > 32'd15) begin
            i_start_up  <= 1'b0     ;
        end
        
        if (wait_cnt > 32'd5) begin  
            o_core_status   <= core_status  ;
            o_exceptions    <= exceptions   ;
            o_exceptions_pc <= exceptions_pc;
        end
    end
end

assign dmem_ce  = EASY_RISCV_C0.U_EASY_RISCV_IEX_LSU.EASY_RISCV_DMEM_BE_U0.ce   ;
assign dmem_we  = EASY_RISCV_C0.U_EASY_RISCV_IEX_LSU.EASY_RISCV_DMEM_BE_U0.we   ;
assign dmem_addr= EASY_RISCV_C0.U_EASY_RISCV_IEX_LSU.EASY_RISCV_DMEM_BE_U0.addr ;
assign dmem_din = EASY_RISCV_C0.U_EASY_RISCV_IEX_LSU.EASY_RISCV_DMEM_BE_U0.din  ;

// And an example sub module. The submodule will print stuff.
EASY_RISCV_TOP EASY_RISCV_C0 (/*AUTOINST*/
    .i_clk              (i_clk              ) 
,   .i_rst_n            (i_rst_n            ) 
,   .i_start_up         (i_start_up         ) 
,   .i_start_pc         (i_start_pc         ) 
,   .i_excp_mask_config (i_excp_mask_config ) 
,   .o_core_status      (core_status        ) 
,   .o_exceptions       (exceptions         ) 
,   .o_exceptions_pc    (exceptions_pc      ) 
);


// Print some stuff as an example
initial begin
    if ($test$plusargs("trace") != 0) begin
        $display("\n[%0t]-- Init ---------------------\n", $time);
        
        $readmemh("./inst.bin.str", EASY_RISCV_C0.U_EASY_RISCV_IFU.U_INSTR_MEM.mem);
        $readmemh("./data.bin.str", EASY_RISCV_C0.U_EASY_RISCV_IEX_LSU.EASY_RISCV_DMEM_BE_U0.mem);
        
        $display("[%0t] Tracing to logs/easy_riscv.vcd...\n", $time);
        $dumpfile("logs/easy_riscv.vcd");
        $dumpvars();
    end
    $display("[%0t] Model running...\n", $time);
end

always @(posedge i_clk or negedge i_rst_n) begin
    if (i_rst_n == 1'b0) begin
        o_sim_end   <= 1'b0 ;  
    end
    // 0x13f0 - 0x400 as print log 
    else if ((dmem_addr == 10'h3FC) && (dmem_we[0] == 1'b1) && (dmem_ce == 1'b1)) begin
        $write("%c", dmem_din[7:0]); 
    end
    // 0x13f1 - 0x400 as sim end 
    else if ((dmem_addr == 10'h3FC) && (dmem_we[1] == 1'b1) && (dmem_ce == 1'b1)) begin
        $display("[%0t] SIM END \n", $time);
        o_sim_end   <= 1'b1 ;  
    end
    // 0x13f2 - 0x400 as testcase status 1: passed, 2: failed 
    else if ((dmem_addr == 10'h3FC) && (dmem_we[2] == 1'b1) && (dmem_ce == 1'b1) && (dmem_din[23:16] == 8'd1)) begin
        $display("\n---------------------\n");
        $display("[%0t] TEST PASSED !!! \n", $time);
    end
    // 0x13f2 - 0x400 as testcase status 1: passed, 2: failed 
    else if ((dmem_addr == 10'h3FC) && (dmem_we[2] == 1'b1) && (dmem_ce == 1'b1) && (dmem_din[23:16] == 8'd2)) begin
        $display("\n---------------------\n");
        $display("[%0t] TEST FAILED !!! \n", $time);
    end
    // 0x13f3 - 0x400 as isa failed testcase num 
    else if ((dmem_addr == 10'h3FC) && (dmem_we[3] == 1'b1) && (dmem_ce == 1'b1)) begin
        $display("[%0t] ISA failed testcase num is %d \n", $time, dmem_din[31:24]);
    end

end

endmodule
